package seedpod.kb2db;

/**
 * Copyright 2009 University of Washington This code is released under the GPL version 3 - with a copy of the license
 * included
 * 
 * @author Maya Hao Li
 */
/**
 * <p>
 * Description:Transforms a Protege model to RDB <b>V2 </b>
 * 
 * @version 2
 * @author Hao Li
 *         </p>
 */

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import seedpod.kb2db.ProjectTransformValidator.TransformValidationException;
import seedpod.model.RdbCls;
import seedpod.model.RdbSlot;
import seedpod.model.SeedpodModel;
import seedpod.model.SeedpodSystemFrames;
import seedpod.model.SeedpodModel.RdbCls.FormWidget;
import seedpod.model.SeedpodModel.RdbCls.ViewWidget;
import seedpod.model.rdb.Attribute;
import seedpod.model.rdb.NoRelationFoundException;
import seedpod.model.rdb.NoViewFoundException;
import seedpod.model.rdb.Rdb;
import seedpod.model.rdb.RdbValueType;
import seedpod.model.rdb.Relation;
import seedpod.model.rdb.SimpleForeignKey;
import seedpod.model.rdb.View;
import seedpod.model.rdb.ViewReference;
import seedpod.util.XMLWriter;
import edu.stanford.smi.protege.model.Cls;
import edu.stanford.smi.protege.model.Frame;
import edu.stanford.smi.protege.model.KnowledgeBase;
import edu.stanford.smi.protege.model.Model;
import edu.stanford.smi.protege.model.Project;
import edu.stanford.smi.protege.model.Slot;
import edu.stanford.smi.protege.model.ValueType;

public class Protege2Rdb {

	public static final String DEFAULT_META_ATTRIBUTE = Model.Cls.STANDARD_SLOT;
	public static final String DEFAULT_META_CLS = Model.Cls.STANDARD_CLASS;
	public static final String DEFAULT_META_RDB_SLOT = SeedpodModel.RdbCls.RDB_SLOT;
	public static final String DEFAULT_META_RDB_CLS = SeedpodModel.RdbCls.RDB_CLASS;

	// List system classes. Note, the classes that are not in-lined will be translated into its own table
	public static String[] PLUGIN_RDB_CLS = { SeedpodModel.RdbCls.TYPE_FILE, SeedpodModel.RdbCls.USER,
			SeedpodModel.RdbCls.ANNOTATION, SeedpodModel.RdbCls.ACCESS_LOG };

	public static final String REIFIED_SLOT_CLS = SeedpodModel.RdbCls.REIFIED_SLOT_CLS;
	public static final String DATA_TYPE_CLS = SeedpodModel.RdbCls.DATA_TYPE;
	public static final int MAX_NAME_LENGTH = Rdb.MAX_NAME_LENGTH;

	public static final String SPECIAL_CONNECTOR = "."; // "\u00b7";
	public static final String THING = Model.Cls.THING;
	public static final String VALID_EXTENSION = ".pprj";

	private static boolean _debug = true;
	@SuppressWarnings("unchecked")
	private static Vector _protegeErrors = new Vector();

	@SuppressWarnings("unchecked")
	public static void displayErrors( Collection errors ) {
		displayErrors( errors, "" );
	}

	@SuppressWarnings("unchecked")
	public static void displayErrors( Collection errors, String prefix ) {
		Iterator i = errors.iterator();
		while (i.hasNext()) {
			print( prefix + i.next() );
		}
	}

	public static void saveProject( Project p ) {
		p.save( _protegeErrors );
		displayErrors( _protegeErrors );
	}

	private static void print( String str ) {
		if ( _debug )
			System.out.println( str );
	}

	protected static String ensureValidNameLength( String name ) {
		name = name.trim();
		if ( name.length() > MAX_NAME_LENGTH ) {
			name = name.substring( 0, MAX_NAME_LENGTH );
		}
		return name;
	}

	protected static String getViewName( String clsName ) {
		return ensureValidNameLength( "v" + SPECIAL_CONNECTOR + clsName );
	}

	protected static boolean isValidRelationCls( Cls cls ) {
		if ( cls.isMetaCls() )
			return false;
		else if ( cls.getTemplateSlots().size() == 0 )
			return false;
		else if ( cls.isAbstract() ) // && cls.getDirectSubclassCount() == 0 )
			return false;
		else
			return true;
	}

	protected static boolean isValidRelationCls( RdbCls rdbCls ) {
		return isValidRelationCls( rdbCls.getCls() );
	}

	/* slots with a domain cls */
	private Vector<RdbSlot> _rdbSlots = new Vector<RdbSlot>();

	private Vector<RdbCls> _rdbClses = new Vector<RdbCls>();
	// private Vector<RdbCls> _inlineClasses = new Vector<RdbCls>(); // complex types
	private Vector<RdbCls> _metaClses = new Vector<RdbCls>();

	private Vector<RdbSlot> _inlineSlots = new Vector<RdbSlot>();
	private ArrayList<String> _errors = new ArrayList<String>();
	/* foreign key slots */
	private Vector<RdbSlot> _fkSlots = new Vector<RdbSlot>();
	private KnowledgeBase _kb;
	private SeedpodSystemFrames _systemFrames;
	private ModelMap _modelMap;
	/* meta classes in the KB */
	private Cls _metaRdbCls;

	private Cls _metaRdbSlot;

	private Project _project;

	private Rdb _rdb;

	private Cls _reifiedSlotCls;

	private HashMap<RdbSlot, RdbCls> _reifiedSlots = new HashMap<RdbSlot, RdbCls>();

	private boolean _validTransformProject = false;

	private boolean _verticalFragmentation = false; // subclass references

	// superclass if true
	private ArrayList<String> _warnings = new ArrayList<String>();

	public Protege2Rdb(KnowledgeBase kb, Rdb database) {
		_kb = kb;
		_rdb = database;
		_modelMap = ModelMap.getInstance();

		_project = _kb.getProject();
		_systemFrames = new SeedpodSystemFrames( _kb );
		_systemFrames.createSeedpodSystemFrames();

		/** initialize all meta_class and meta_slot instances * */
		_metaRdbCls = _kb.getCls( DEFAULT_META_RDB_CLS );
		_metaRdbSlot = _kb.getCls( DEFAULT_META_RDB_SLOT );
	}

	/**
	 * erase classes created during the transformation under reified_slot_cls
	 */
	@SuppressWarnings("unchecked")
	public void cleanup() {
		if ( _reifiedSlotCls == null )
			_reifiedSlotCls = _kb.getCls( REIFIED_SLOT_CLS );

		if ( _reifiedSlotCls == null )
			return;

		Collection reifiedClses = _reifiedSlotCls.getSubclasses();
		System.err.println( "Transform clean up..." );
		for ( Iterator clsIter = reifiedClses.iterator(); clsIter.hasNext(); ) {
			Cls cls = (Cls)clsIter.next();

			System.err.println( "delete temp cls " + cls.getName() );
			Collection slots = cls.getTemplateSlots();
			for ( Iterator slotItr = slots.iterator(); slotItr.hasNext(); ) {
				Slot slot = (Slot)slotItr.next();
				_kb.deleteSlot( slot );
			}

			_kb.deleteCls( cls );
		}
		System.err.println( "Transform clean up completed." );
	}

	public ModelMap getMapping() {
		return _modelMap;
	}

	public Project getProject() {
		return _project;
	}

	public Collection<String> getTransformErrors() {
		return _errors;
	}

	public Collection<String> getTransformWarnings() {
		return _warnings;
	}

	public boolean isProjectValid() {
		return _validTransformProject;
	}

	/**
	 * print function for debugging.
	 */
	public void serialize( PrintStream pw ) {
		RdbCls rdbCls;
		RdbSlot rdbSlot;
		Vector<RdbSlot> slotVec;
		XMLWriter slotXmlOut;
		XMLWriter xmlOut = new XMLWriter( "protege2rdb" );

		try {
			xmlOut.addElement( "kb", _kb.getName() );
			xmlOut.addElement( "_verticalFragmentation", Boolean.toString( _verticalFragmentation ) );

			/* print cls */
			for ( int i = 0; i < _rdbClses.size(); i++ ) {
				rdbCls = ((RdbCls)_rdbClses.elementAt( i ));
				XMLWriter clsOut = xmlOut.addElement( "class" );
				clsOut.addElement( "FrameID", rdbCls.getCls().getFrameID().toString() );
				clsOut.addElement( "Name", rdbCls.getName() );

				/* print slot */
				slotVec = getTemplateSlots( rdbCls );
				slotXmlOut = clsOut.addElement( "slots" );
				for ( int a = 0; a < slotVec.size(); a++ ) {
					rdbSlot = (RdbSlot)slotVec.elementAt( a );
					rdbSlot.toXML( slotXmlOut );
				}
			}
		}
		catch (seedpod.util.XMLWriter.XMLException xmle) {
			xmle.printStackTrace();
		}

	}

	/**
	 * sets the transformation of inheritance into vertical fragmentation.
	 * 
	 * @param vf default is false.
	 */
	public void setVerticalFragmentation( boolean vf ) {
		_verticalFragmentation = vf;
	}

	/**
	 * Transforms the instances of this meta class, (classes), to a RDB model
	 */
	public void transform() {
		// Step 0. initialize
		init();

		// Step 1. hide one of the inverse slot pairs
		mapInverseSlots();

		// Step 2. reify slots with maximum cardinality
		reifySlotsWithMaxCardinality();

		// Step 3. map cls to relations and views
		mapClsesToRelations();
		mapClsesToViews();
		mapMetaClses();

		// Step 4a. map assocSlots to attributes with corresponding class->table
		mapSlotsToAttributes();
		mapInlineSlotsToAttributes();

		// Step 4b. map slots to relations
		mapSlotsToRelations();

		// Step 4c. add foreign keys
		mapSlotsToForeignKeys();

		// serialize( getPrintStream() );
		System.out.println( "Transformation complete." );
	}

	/**
	 * Mapping a rdbSlot that's of a complex type that's in-lined: create new attribute in the table for each of the
	 * slots defined in the complex type. Create a mapping in ModelMap between the new attribute and the Limitation :
	 * ONLY atomic types can be in-lined
	 */
	protected void mapInlineSlotsToAttributes() {
		for ( RdbSlot containerRdbSlot : _inlineSlots ) {
			System.err.println( "mapping inLine slot : " + containerRdbSlot.getName() );
			Collection<Slot> templateSlots = containerRdbSlot.getAllowedCls().getTemplateSlots();
			Cls domainCls = containerRdbSlot.getDomainCls();

			for ( Iterator<Slot> iter = templateSlots.iterator(); iter.hasNext(); ) {
				Slot definitionSlot = iter.next();

				// limit in-lined attributes to only atomic types (no instance or cls)
				if ( !RdbSlot.isAtomicType( definitionSlot ) )
					continue;

				String inlineSlotName = containerRdbSlot.getName() + "." + definitionSlot.getName();

				// create a new Slot which is named like the new inlineAttribute
				Slot inlineSlot = _kb.createSlot( inlineSlotName, _metaRdbSlot );
				domainCls.addDirectTemplateSlot( inlineSlot );
				inlineSlot.setValueType( definitionSlot.getValueType() );
				inlineSlot.setMinimumCardinality( 0 );
				inlineSlot.setMaximumCardinality( 1 );
				inlineSlot.setDocumentation( inlineSlot.getDocumentation() );

				// create a RdbSlot using the new in-line slot
				RdbSlot inlineRdbSlot = new RdbSlot( inlineSlot, domainCls, _kb );

				// create an inlineAttribute
				Attribute inlineAttribute = createInlineAttribute( inlineRdbSlot, containerRdbSlot );

				_rdbSlots.add( inlineRdbSlot );
				_modelMap.addMapping( inlineRdbSlot, inlineAttribute );

				System.out.println( "        -> " + inlineSlot.getName() );
			}
			containerRdbSlot.setIsAssociated( false );
		}

	}

	protected void mapMetaClses() {
		for ( int i = 0; i < _metaClses.size(); i++ ) {
			RdbCls metaCls = _metaClses.elementAt( i );
			_modelMap.addMetaCls( metaCls );
		}
	}

	public boolean validateKB() {
		ProjectTransformValidator validator = new ProjectTransformValidator( _kb );
		_validTransformProject = false;
		try {
			_validTransformProject = validator.validateKB();
		}
		catch (TransformValidationException te) {
			_errors.addAll( validator.getErrors() );
		}
		return _validTransformProject;
	}

	private void addReifiedSlot( RdbSlot rdbSlot, RdbCls associationCls ) {
		_reifiedSlots.put( rdbSlot, associationCls );
	}

	private void changeUserDefinedDatabaseType( RdbSlot rdbSlot, RdbValueType dbtype ) {
		RdbValueType currType = rdbSlot.getDatabaseType();
		rdbSlot.setDatabaseType( dbtype );
		_warnings.add( rdbSlot.getName() + " database type changed from " + currType.toString() + " to "
				+ dbtype.toString() );

	}

	private Slot createAssocClsSlot( boolean isTargetSlot, String instanceClsName, ValueType type ) {
		String dest = (isTargetSlot) ? "TO" : "FROM";

		String slotName = createReifiedClsSlotName( dest, instanceClsName );
		Slot slot = _kb.getSlot( slotName );
		if ( slot == null )
			slot = _kb.createSlot( slotName, _metaRdbSlot );
		else if ( slot.getDomain().size() != 0 )
			slot = _kb.getSlot( null );

		slot.setValueType( type );
		slot.setMinimumCardinality( 1 );
		slot.setMaximumCardinality( 1 );
		return slot;
	}

	private Cls createAssociationCls(	RdbSlot assocSlot,
										String associationClsName,
										String targetName,
										String sourceName,
										ValueType targetValueType ) {
		Cls associationCls;
		Slot targetSlot, sourceSlot;
		RdbSlot targetRdbSlot, sourceRdbSlot;
		boolean isTargetInstance = (targetValueType.compareTo( ValueType.INSTANCE ) == 0);

		// association parent class: reifiedSlotCls
		Vector<Cls> assocClsParent = new Vector<Cls>();
		assocClsParent.add( _reifiedSlotCls );
		associationCls = _kb.createCls( associationClsName, assocClsParent, _metaRdbCls );

		// target slot can be instance or primitive type
		targetSlot = createAssocClsSlot( true, targetName, targetValueType );
		targetSlot.setVisible( true );
		targetRdbSlot = new RdbSlot( targetSlot, associationCls, _kb );
		targetRdbSlot.setIsAssociated( true );

		if ( isTargetInstance ) {
			targetSlot.setAllowedClses( assocSlot.getAllowedClses() );
		} else {
			targetRdbSlot.setDatabaseType( mapProtegeValueTypeToRdbType( assocSlot ) );
			targetRdbSlot.setDatabaseTypeParameter( assocSlot.getDatabaseTypeParameter() );
			targetRdbSlot.setFormParameter( assocSlot.getFormWidget() );
			targetRdbSlot.setFormParameter( assocSlot.getFormWidgetParameter() );
			if ( targetValueType.equals( ValueType.SYMBOL ) )
				targetSlot.setAllowedValues( assocSlot.getSlot().getAllowedValues() );
		}
		associationCls.addDirectTemplateSlot( targetSlot );

		// create From slot, which is an instance of the referencing class
		sourceSlot = createAssocClsSlot( false, sourceName, ValueType.INSTANCE );
		Vector<Cls> sourceSlotAllowedClses = new Vector<Cls>();
		sourceSlotAllowedClses.add( assocSlot.getDomainCls() );
		sourceSlot.setAllowedClses( sourceSlotAllowedClses );
		sourceSlot.setVisible( true );
		associationCls.addDirectTemplateSlot( sourceSlot );

		// hide the current assocSlot from being mapped b/c it's not an assocCls
		assocSlot.setIsAssociated( false );
		sourceRdbSlot = new RdbSlot( sourceSlot, associationCls, _kb );
		sourceRdbSlot.setIsAssociated( true );

		// add the new reference slots and cls to the list to be mapped
		_rdbClses.add( new RdbCls( associationCls, _kb ) );
		_rdbSlots.add( targetRdbSlot );
		_rdbSlots.add( sourceRdbSlot );
		return associationCls;
	}

	private String createReifiedClsSlotName( String pre, String clsName ) {
		int frameCount = _kb.getFrameCount();
		int padding = pre.length() + SPECIAL_CONNECTOR.length() * 2 + Integer.toString( frameCount ).length();
		int fullLength = clsName.length() + padding;

		if ( fullLength > MAX_NAME_LENGTH )
			clsName = clsName.substring( 0, MAX_NAME_LENGTH - padding );

		return (pre + SPECIAL_CONNECTOR + clsName + SPECIAL_CONNECTOR + _kb.getFrameCount());
	}

	private void createThingRelation() {
		Relation thingRel = _rdb.createThingRelation( Protege2Rdb.THING );
		Cls thingCls = _kb.getCls( THING );
		_modelMap.addMapping( new RdbCls( thingCls, _kb ), thingRel );

		// add thing SlotMap to ModelMap
		Vector<Attribute> tAtt = thingRel.getAttributes();
		for ( int i = 0; i < tAtt.size(); i++ ) {
			Attribute currAtt = tAtt.elementAt( i );
			Slot dummyslot = _kb.getSlot( currAtt.getName() );
			if ( dummyslot == null )
				dummyslot = _kb.createSlot( currAtt.getName() );

			_modelMap.addMapping( new RdbSlot( dummyslot, thingCls, _kb ), currAtt );
		}
	}

	/**
	 * Deletes the cls and its template slots entirely from knowledge base <br>
	 * TODO debug to make sure that the deletion really worked
	 */
	@SuppressWarnings("unchecked")
	private void deleteClsAndSlots( Cls cls ) {
		Slot slot;
		RdbSlot tmpSlot;
		RdbCls rdbCls = new RdbCls( cls, _kb );
		for ( Iterator slotIter = cls.getTemplateSlots().iterator(); slotIter.hasNext(); ) {
			slot = (Slot)slotIter.next();
			tmpSlot = new RdbSlot( slot, cls, _kb );
			_warnings.add( "deleting slot " + slot.getName() );
			_rdbSlots.remove( tmpSlot );
			_kb.deleteSlot( slot );
			if ( _kb.getSlot( slot.getName() ) != null )
				_warnings.add( "_kb failed to delete slot " + slot.getName() );
		}

		_kb.deleteCls( cls );
		if ( _kb.getCls( cls.getName() ) != null )
			print( "_kb failed to delete class " + cls.getName() );
		_rdbClses.remove( rdbCls );
	}

	/**
	 * Returns all template slots of cls (including inherited slots)
	 * 
	 * @param cls
	 * @return vector of AssociatedSlot
	 */
	private Vector<RdbSlot> getTemplateSlots( RdbCls rdbCls ) {
		Cls domainCls = rdbCls.getCls();

		Vector<RdbSlot> slots = new Vector<RdbSlot>();
		for ( int i = 0; i < _rdbSlots.size(); i++ ) {
			RdbSlot currSlot = (RdbSlot)_rdbSlots.elementAt( i );
			if ( currSlot.getDomainCls().equals( domainCls ) )
				slots.add( currSlot );
		}
		return slots;
	}

	/**
	 * initialize all classes of root meta-class type initialize map
	 */
	private void init() {
		initRdbClses();
		initPluginClses();
		initMetaClses();
		initRdbSlots();
		initReifiedSlotRootCls();

		initThingCls();
		createThingRelation();
	}

	/*
	 * rdbCls to be converted to tables if it's not in-lined, meta classes included.
	 */
	private void initRdbCls( Cls cls ) {
		RdbCls rdbCls = new RdbCls( cls, _kb );
		ensureValidNameLength( rdbCls );

		if ( !_rdbClses.contains( rdbCls ) ) {
			if ( !rdbCls.isInline() )
				_rdbClses.add( rdbCls );
		}
	}

	@SuppressWarnings("unchecked")
	private void initRdbClses() {
		Cls cls;
		Collection rdbClasses = _metaRdbCls.getInstances();
		Iterator clsItr = rdbClasses.iterator();

		// get all classes that will be transformed, filter out meta-classes
		while (clsItr.hasNext()) {
			cls = (Cls)clsItr.next();
			cls.setName( cls.getName().replaceAll( "'", "_" ) );
			initRdbCls( cls );
		}
	}

	@SuppressWarnings("unchecked")
	protected void initMetaClses() {
		Collection subMetaClses = _metaRdbCls.getSubclasses();
		Iterator clsItr = subMetaClses.iterator();
		while (clsItr.hasNext()) {
			Cls cls = (Cls)clsItr.next();
			cls.setName( cls.getName().replaceAll( "'", "_" ) );
			RdbCls metaCls = new RdbCls( cls, _kb );
			_metaClses.add( metaCls );
		}
	}

	/**
	 * The associSlots are slots that have defined domain classes. A table contains direct template slots when vertical
	 * fragmentation is selected.
	 */
	private void initRdbSlots() {
		Cls cls;
		Slot slot;
		Iterator<Slot> templateSlotItr;
		RdbSlot rdbSlot;
		for ( int c = 0; c < _rdbClses.size(); c++ ) {
			cls = ((RdbCls)_rdbClses.elementAt( c )).getCls();

			if ( _verticalFragmentation )
				templateSlotItr = cls.getDirectTemplateSlots().iterator();
			else
				templateSlotItr = cls.getTemplateSlots().iterator();

			while (templateSlotItr.hasNext()) {
				slot = (Slot)templateSlotItr.next();
				if ( slot.hasType( _metaRdbSlot ) ) {
					slot.setName( slot.getName().replaceAll( "'", "_" ) );
					slot.setVisible( true );
					rdbSlot = new RdbSlot( slot, cls, _kb );
					ensureValidNameLength( rdbSlot );
					_rdbSlots.add( rdbSlot );
				}
			}
		}
	}

	/**
	 * This is a system class that contains reified slot classes. It's contained in the system class tree because they
	 * should be hidden from the users. The parent class ":REIFIED_SLOT_CLS" is abstract. The class uses :RDB_CLS as
	 * meta-class.
	 */
	private void initReifiedSlotRootCls() {
		_reifiedSlotCls = _kb.getCls( REIFIED_SLOT_CLS );

		if ( _reifiedSlotCls == null ) {

			// create reifiedCls to KB
			Vector<Cls> rParent = new Vector<Cls>();
			rParent.add( _kb.getCls( Model.Cls.SYSTEM_CLASS ) );
			_reifiedSlotCls = _kb.createCls( REIFIED_SLOT_CLS, rParent, _metaRdbCls );
			_reifiedSlotCls.setAbstract( true );
			_reifiedSlotCls.setVisible( true );
		}
	}

	private void initThingCls() {
		_rdbClses.add( new RdbCls( _kb.getCls( THING ), _kb ) );
	}

	/**
	 * In the case the a particular association class already exist for a relationship slot, this is to check if the cls
	 * is the correct association class created for the slot. check the following:<br>
	 * <li>has the domain classes changed? <li>has the target class/value changed? <li>both have the from and to slots
	 * have correct cardinality
	 * 
	 * @param cls existing association cls
	 * @param rdbSlot association slot that is reified to the cls
	 * @return true if the cls matches reification outcome of the slot
	 */
	@SuppressWarnings("unchecked")
	private boolean isAssociationClsForReifiedSlot( Cls cls, RdbSlot rdbSlot, String targetClsName, String sourceClsName ) {
		Iterator slotItr = _kb.getDirectTemplateSlots( cls ).iterator();
		boolean srcSlotCheck = false;
		boolean tgtSlotCheck = false;
		Slot slot;
		while (slotItr.hasNext()) {
			slot = (Slot)slotItr.next();
			Cls allowedCls = null;
			Iterator clsItr;
			if ( slot.getValueType().compareTo( ValueType.INSTANCE ) == 0 ) {
				clsItr = slot.getAllowedClses().iterator();
				if ( clsItr.hasNext() )
					allowedCls = (Cls)clsItr.next();
			}

			if ( slot.getName().startsWith( "FROM" ) ) {
				if ( allowedCls == null )
					srcSlotCheck = false;
				else
					srcSlotCheck = (allowedCls.getName().compareTo( sourceClsName ) == 0
							&& slot.getMaximumCardinality() == 1 && slot.getMinimumCardinality() == 1);

			} else if ( slot.getName().startsWith( "TO" ) ) {
				if ( allowedCls != null )
					tgtSlotCheck = (allowedCls.getName().compareTo( targetClsName ) == 0);
				else {
					_warnings.add( "WARNING: reified slot " + slot.getName() + " has primitive value type - "
							+ slot.getValueType().toString() );
					tgtSlotCheck = (slot.getValueType().toString() == targetClsName);
				}

				tgtSlotCheck = (tgtSlotCheck && slot.getMaximumCardinality() == 1 && slot.getMinimumCardinality() == 1);

			}
		}

		if ( !srcSlotCheck ) {
			String err0 = "Transformation of slot " + rdbSlot.getName() + " doesn't match cls " + cls.getName()
					+ ":sourceSlot";
			print( err0 );
			_errors.add( err0 );
		}

		if ( !tgtSlotCheck ) {
			String err1 = "Transformation of slot " + rdbSlot.getName() + " doesn't match cls " + cls.getName()
					+ ":targetSlot";
			print( err1 );
			_errors.add( err1 );
		}

		return (srcSlotCheck && tgtSlotCheck);
	}

	private boolean isReifiedSlotCls( Cls cls ) {
		return (cls.hasSuperclass( _kb.getCls( REIFIED_SLOT_CLS ) ));
	}

	private void mapFormWidget( RdbSlot rdbSlot, Attribute att ) {
		ValueType pType = rdbSlot.getValueType();
		RdbValueType dbType = rdbSlot.getDatabaseType();
		String formWidget = rdbSlot.getFormWidget();

		if ( formWidget == null || formWidget.length() == 0 ) {
			if ( pType.equals( ValueType.BOOLEAN ) ) {
				formWidget = FormWidget.RADIO.toString();
			} else if ( pType.equals( ValueType.SYMBOL ) ) {
				formWidget = FormWidget.SELECT.toString();
			} else if ( pType.equals( ValueType.BOOLEAN ) ) {
				formWidget = FormWidget.BOOLEAN.toString();
			} else if ( dbType.equals( RdbValueType.DATE ) ) {
				formWidget = FormWidget.DATE.toString();
			} else if ( dbType.equals( RdbValueType.SERIAL ) ) {
				formWidget = FormWidget.AUTO_INCREMENT.toString();
			} else if ( dbType.equals( RdbValueType.TIMESTAMP ) ) {
				formWidget = FormWidget.TIMESTAMP.toString();
			} else if ( pType.equals( ValueType.FLOAT ) || pType.equals( ValueType.INTEGER )
					|| pType.equals( ValueType.STRING ) )
				formWidget = FormWidget.TEXT.toString();
			else if ( pType.equals( ValueType.INSTANCE ) )
				formWidget = FormWidget.OBJECT_LINK.toString();
		}
		rdbSlot.setFormWidget( formWidget );
		att.setFormWidget( formWidget );
	}

	// VIEW_STRING, VIEW_DATE, VIEW_OBJECTLINK, VIEW_RADIO, VIEW_CHECKBOX, VIEW_FILE, VIEW_NUMERIC, VIEW_NUMERIC
	private void mapViewWidget( RdbSlot rdbSlot, Attribute att ) {
		String viewWidget = rdbSlot.getViewWidget();
		ValueType pType = rdbSlot.getValueType();
		

		if ( viewWidget == null || viewWidget.length() == 0 ) {
			// set default view if not defined
			if ( pType.equals( ValueType.BOOLEAN ) ) {
				viewWidget = ViewWidget.RADIO.toString();
			} else if ( pType.equals( ValueType.INTEGER ) || pType.equals( ValueType.STRING ) )
				viewWidget = ViewWidget.STRING.toString();
			else if ( pType.equals( ValueType.SYMBOL ) )
				viewWidget = ViewWidget.STRING.toString();
			else if ( pType.equals( ValueType.FLOAT ) )
				viewWidget = ViewWidget.NUMERIC.toString();
			else if ( pType.equals( ValueType.INSTANCE ) )
				viewWidget = ViewWidget.OBJECT_LINK.toString();
		}

		att.setViewWidget( viewWidget );
		rdbSlot.setViewWidget( viewWidget );
	}

	/**
	 * iterate through all associated slots,cache the ones that have inverse slots then drop one of the inverse-slot
	 * pairs TODO ASSUME not inverse many-to-many relations
	 */
	private void mapInverseSlots() {
		/* temporary hash that keeps slots with associated inverse slot. */
		HashMap<Slot, RdbSlot> slotHasInverseSlot = new HashMap<Slot, RdbSlot>();

		RdbSlot rdbSlot, invRdbSlot;
		Slot slot, invSlot;

		for ( int i = 0; i < _rdbSlots.size(); i++ ) {
			rdbSlot = (RdbSlot)_rdbSlots.elementAt( i );
			slot = rdbSlot.getSlot();
			invSlot = slot.getInverseSlot();

			if ( slotHasInverseSlot.containsKey( slot ) ) {
				invRdbSlot = (RdbSlot)slotHasInverseSlot.get( slot );
				rdbSlot.setInverse( invRdbSlot );
				invRdbSlot.setInverse( rdbSlot );

				reifyOneSlotInversePair( rdbSlot, invRdbSlot );

				slotHasInverseSlot.remove( invSlot );
				slotHasInverseSlot.remove( rdbSlot.getSlot() );
			} else if ( invSlot != null ) {
				slotHasInverseSlot.put( invSlot, rdbSlot );
				System.err.println( " ===== " + invSlot.getName() + " --> " + rdbSlot.getName() + " in "
						+ rdbSlot.getDomainCls().getName() );
			}
		}
	}

	/**
	 * order _rdbclasses such that leaf classes are visited before their parent clses
	 */
	@SuppressWarnings("unchecked")
	private void postOrderSortClassElements() {
		RdbCls currCls, superCls;
		int currClsIndex, superClsIndex;

		Vector<RdbCls> orderList = _rdbClses;

		for ( int i = 0; i < _rdbClses.size(); i++ ) {
			currCls = (RdbCls)_rdbClses.elementAt( i );

			Iterator superClsIter = currCls.getCls().getSuperclasses().iterator();
			currClsIndex = orderList.indexOf( currCls );

			while (superClsIter.hasNext()) {
				superCls = new RdbCls( (Cls)superClsIter.next(), _kb );
				if ( _rdbClses.contains( superCls ) ) {
					superClsIndex = orderList.indexOf( superCls );

					if ( currClsIndex > superClsIndex ) {
						orderList.removeElement( currCls );
						orderList.insertElementAt( currCls, superClsIndex );

						currClsIndex = superClsIndex;
					}
				}
			}
		}

		_rdbClses = orderList;
	}

	/**
	 * choose one of the inverse slot pair to reify and hide the other one TODO test inverse mapping
	 */
	private void reifyOneSlotInversePair( RdbSlot slot1, RdbSlot slot2 ) {
		// map the required slot and/or slot with max cardinality
		/**
		 * TODO add check constraints for min =1 required from both relations
		 */
		if ( slot1.allowsMultipleValues() ) {
			slot1.setIsAssociated( false );
			slot2.setIsAssociated( true );

			// _rdbSlots.remove( slot1 );
			System.err.println( " ===---" + slot1.getDomainCls().getName() + "." + slot1.getName() + " not Associated" );
		} else {
			slot1.setIsAssociated( true );
			slot2.setIsAssociated( false );

			// _rdbSlots.remove( slot2 );
			System.err.println( " ===---" + slot2.getDomainCls().getName() + "." + slot2.getName() + " not Associated" );
		}
	}

	/**
	 * create an inline attribute based on slot in domain Cls's table
	 * 
	 * @param inlineSlotOrigin
	 * @param slotContainer The slot that contains the complex type slot
	 */
	@SuppressWarnings("unchecked")
	protected Attribute createInlineAttribute( RdbSlot inlineSlot, RdbSlot slotContainer ) {
		Attribute inlineAtt = null;
		Cls domainCls = slotContainer.getDomainCls();

		RdbValueType inlineSlotDbType = mapProtegeValueTypeToRdbType( inlineSlot );
		Relation rel;
		View view;

		// add in-line slot to the slotContainer's containing relation
		try {
			rel = _rdb.getRelation( domainCls.getName() );
			inlineAtt = _rdb.addAttribute( rel, inlineSlot.getName(), inlineSlotDbType, false );
		}
		catch (NoRelationFoundException e) {
			_warnings.add( "NoRelationFoundException in createInlineAttribute for " + inlineSlot.getName() + " "
					+ e.getMessage() );
		}

		// add in-line slot to view
		try {
			if ( isValidViewCls( domainCls ) ) {
				view = _rdb.getView( getViewName( domainCls.getName() ) );
				if ( inlineAtt == null )
					inlineAtt = _rdb.addAttribute( view, inlineSlot.getName(), inlineSlotDbType, false );
				else
					view.addAttribute( inlineAtt );
			}
		}
		catch (NoViewFoundException e1) {
			_errors.add( "NoViewFoundException in createInlineAttribute " + e1.getMessage() );
		}

		// initialize attribute properties for the new in-lined attributed
		if ( inlineAtt != null ) {
			mapDatabaseParameter( slotContainer, inlineAtt );
			mapFormWidget( slotContainer, inlineAtt );
			inlineAtt.setFormWidgetParameter( slotContainer.getFormWidgetParameter() );
			mapViewWidget( slotContainer, inlineAtt );
			inlineAtt.setViewWidgetParameter( slotContainer.getViewWidgetParameter() );
			inlineAtt.setViewSequence( slotContainer.getViewSequence() );
			inlineAtt.setIsRequired( slotContainer.isRequired() );
			inlineAtt.setDefaultValue( slotContainer.getDefaultValue() );
			inlineAtt.setAllowedValues( slotContainer.getAllowedValues() );
			inlineAtt.setIsImplemented( true );

			_modelMap.addMapping( slotContainer, inlineAtt );
			System.err.println( slotContainer.getName() + " --> " + inlineAtt.getName() );
		}
		return inlineAtt;
	}

	/**
	 * returns boolean true if the name has been changed.
	 * 
	 * @param frame
	 * @return
	 */
	protected boolean ensureValidNameLength( Frame frame ) {
		boolean nameChanged = false;
		String name = frame.getName();

		if ( name.length() > MAX_NAME_LENGTH ) {
			nameChanged = true;
			String old = name;
			name = name.substring( 0, MAX_NAME_LENGTH );

			frame.setName( name );
			_warnings.add( "[NameTooLongChange]: " + old + " is changed " + name );
		}
		return nameChanged;
	}

	protected void ensureValidNameLength( RdbCls rdbCls ) {
		Cls cls = rdbCls.getCls();
		String originalName = cls.getName();
		String userName = rdbCls.getUserDefinedName();
		if ( ensureValidNameLength( cls ) && (userName == null || userName.length() == 0) ) {
			rdbCls.setUserDefinedName( originalName );
		}
	}

	protected void ensureValidNameLength( RdbSlot rdbSlot ) {
		Slot slot = rdbSlot.getSlot();
		String originalName = slot.getName();
		String userName = rdbSlot.getUserDefinedName();
		if ( ensureValidNameLength( slot ) && (userName == null || userName.length() == 0) ) {
			rdbSlot.setUserDefinedName( originalName );
		}
	}

	/*
	 * valid instance Cls value type are Clses that are meta-rdbCls and THING
	 */
	protected boolean hasValidInstanceClsValueType( RdbSlot assocSlot ) {
		if ( assocSlot.getValueType().equals( ValueType.ANY ) )
			return false;

		Cls typeCls = assocSlot.getAllowedCls();

		if ( typeCls != null && typeCls.hasType( _metaRdbCls ) )
			return true;
		else if ( typeCls != null && typeCls.getName().compareTo( THING ) == 0 )
			return true;
		else {
			assocSlot.setIsAssociated( false, "Slot instance value type is not of database type." );
			return false;
		}
	}

	protected void initPluginClses() {
		Cls cls;
		for ( int i = 0; i < PLUGIN_RDB_CLS.length; i++ ) {
			cls = _kb.getCls( PLUGIN_RDB_CLS[i] );
			if ( cls != null ) {
				cls.setName( cls.getName().replaceAll( "'", "_" ) );
				initRdbCls( cls );
			}
		}
	}

	/**
	 * whether slot should be reified. current conditions are: 1. associated with a domain 2. has multiple values
	 * instance type Note: assume the
	 * 
	 * @param assocSlot
	 * @return true if slot should become a table
	 */
	protected boolean isSlotReifiable( RdbSlot assocSlot ) {
		if ( !assocSlot.allowsMultipleValues() || !assocSlot.hasAssociatedCls() )
			return false;
		else if ( !assocSlot.isAtomicType() && !hasValidInstanceClsValueType( assocSlot ) )
			return false;
		else if ( assocSlot.referencesInlineCls() )
			return false;
		else if ( _rdb.supportsArrayType() && assocSlot.isAtomicType() )
			return false;
		else
			return true;
	}

	/*
	 * a class has a view if it's not a meta class and if it's one or more template slots
	 */
	protected boolean isValidViewCls( final Cls cls ) {
		if ( cls.isMetaCls() || isSystem( cls ) ) {
			return false;
		} else if ( cls.getTemplateSlots().size() == 0 ) {
			return false;
		} else if ( isReifiedSlotCls( cls ) ) {
			return false;
		} else if ( cls.isAbstract() && cls.getDirectSubclassCount() == 0 ) {
			return false;
		} else
			return true;

	}

	protected boolean isSystem( Cls cls ) {
		return (cls.isSystem() || cls.hasSuperclass( _kb.getCls( Model.Cls.SYSTEM_CLASS ) ));
	}

	protected boolean isValidViewCls( final RdbCls rdbCls ) {
		return isValidViewCls( rdbCls.getCls() );
	}

	/**
	 * Check if a class is valid to be mapped to a relation, otherwise, it's added to the meta class queue to be
	 * processed later
	 */
	protected void mapClsesToRelations() {
		RdbCls cls;
		for ( int c = 0; c < _rdbClses.size(); c++ ) {
			cls = (RdbCls)_rdbClses.elementAt( c );
			if ( isValidRelationCls( cls ) )
				mapClsToRelation( cls );
			else
				_metaClses.add( cls );
		}
	}

	// create views for each class, post order sort the class tree to avoid
	// dependence problem
	protected void mapClsesToViews() {
		RdbCls rdbCls;
		postOrderSortClassElements();

		for ( int v = 0; v < _rdbClses.size(); v++ ) {
			rdbCls = (RdbCls)_rdbClses.elementAt( v );
			if ( isValidViewCls( rdbCls ) )
				mapClsToView( rdbCls );
		}
	}

	/**
	 * 3b) Make a copy (with rename) non-leaF concrete class 3c) add views that select union from all children class and
	 * the parent from 3b) 4) class -> tables TEST check that the classes that have children and some children is
	 * concrete
	 * 
	 * @param cls Input class
	 */
	protected void mapClsToRelation( RdbCls rdbCls ) {
		boolean reifiedSlot = rdbCls.getCls().hasSuperclass( _reifiedSlotCls );
		boolean setDefaultPK = !reifiedSlot;
		Relation rel = _rdb.createRelation( rdbCls.getName(), setDefaultPK );
		_modelMap.addMapping( rdbCls, rel );
	}

	/**
	 * Every Cls has a view, renamed to v_[className]
	 * 
	 * @param rdbCls
	 */
	@SuppressWarnings("unchecked")
	protected void mapClsToView( RdbCls rdbCls ) {
		Cls cls = rdbCls.getCls();
		Cls subcls;
		View subClsView;

		View clsView = _rdb.createView( getViewName( cls.getName() ), true );
		try {
			if ( cls.isConcrete() ) {
				Relation concreteRel = _rdb.getRelation( cls.getName() );
				clsView.addToUnionRelation( concreteRel );
				clsView.setConcreteClassTable( concreteRel );
			} else {

			}

			// add subCls to the clsView for union
			for ( Iterator subClsItr = cls.getDirectSubclasses().iterator(); subClsItr.hasNext(); ) {
				subcls = (Cls)subClsItr.next();

				if ( isValidViewCls( subcls ) ) {
					subClsView = _rdb.getView( getViewName( subcls.getName() ) );
					clsView.addToUnionRelation( subClsView );
				}
			}
		}
		catch (NoRelationFoundException nre) {
			_errors.add( "error in mapClsToView " + nre.getMessage() );
		}
		catch (NoViewFoundException nve) {
			_errors.add( "error in mapClsToView " + nve.getMessage() );
		}

		// map class and view
		_modelMap.addMapping( rdbCls, clsView );
	}

	// automatically generated dbParams may be created for both rdbSlot and att
	@SuppressWarnings("unchecked")
	protected void mapDatabaseParameter( RdbSlot rdbSlot, Attribute att ) {
		String dbParam = rdbSlot.getDatabaseTypeParameter();
		ValueType pType = rdbSlot.getValueType();
		RdbValueType dbType = rdbSlot.getDatabaseType();
		boolean setAutoParam = true;

		// set symbolic value parameter to the longest length of the symbols
		if ( pType.equals( ValueType.SYMBOL ) ) {
			Collection allowedValues = rdbSlot.getAllowedValues();
			int longestLength = 0;
			for ( Iterator iter = allowedValues.iterator(); iter.hasNext(); ) {
				String val = (String)iter.next();
				if ( val.length() > longestLength ) {
					longestLength = val.length();
				}
			}

			if ( dbParam == null || dbParam.length() == 0 )
				setAutoParam = true;
			else if ( Integer.parseInt( dbParam ) == longestLength )
				setAutoParam = false;

			if ( setAutoParam ) {
				rdbSlot.setFormParameter( String.valueOf( longestLength ) );
				att.setDatabaseTypeParameter( String.valueOf( longestLength ) );
				_warnings.add( "Database type parameter changed to " + longestLength + " for " + rdbSlot.getName()
						+ " of " + rdbSlot.getDomainCls().getName() );
			}
		} else if ( pType.equals( ValueType.STRING ) ) {
			if ( dbType.equals( RdbValueType.CHARACTER ) || dbType.equals( RdbValueType.VARCHAR ) ) {
				if ( dbParam.length() == 0 ) {
					dbParam = Integer.toString( RdbSlot.DEFAULT_VARCHAR_LENGTH );
					_warnings.add( rdbSlot.getName() + " database type parameter changed to " + dbParam );
				}
			} else if ( dbType.equals( RdbValueType.TEXT ) )
				dbParam = "";

			att.setDatabaseTypeParameter( dbParam );
			rdbSlot.setDatabaseTypeParameter( dbParam );
		} else if ( !dbType.equals( RdbValueType.CHARACTER ) && !dbType.equals( RdbValueType.VARCHAR ) ) {
			att.setDatabaseTypeParameter( "" );
		} else {
			att.setDatabaseTypeParameter( dbParam );
		}
	}

	/**
	 * Maps atomic protege value type to RDB value type TODO should throw unknown type error
	 * 
	 * @param type protege ValueType object
	 * @return RdbValueType of the corresponding Protege ValueType. It will also look into the
	 */
	protected RdbValueType mapProtegeValueTypeToRdbType( RdbSlot rdbSlot ) {
		RdbValueType dbtype;
		RdbValueType userType = rdbSlot.getDatabaseType();
		ValueType kbtype = rdbSlot.getSlot().getValueType();

		if ( userType.equals( RdbValueType.SERIAL ) || userType.equals( RdbValueType.DATE )
				|| userType.equals( RdbValueType.TIME ) || userType.equals( RdbValueType.TIMESTAMP ) )
			dbtype = userType;
		else if ( kbtype.equals( ValueType.BOOLEAN ) ) {
			dbtype = RdbValueType.BOOLEAN;
		} else if ( kbtype.equals( ValueType.INTEGER ) ) {
			dbtype = RdbValueType.INTEGER;
		} else if ( kbtype.equals( ValueType.FLOAT ) )
			dbtype = RdbValueType.NUMERIC;
		else if ( kbtype.equals( ValueType.STRING ) ) {
			if ( userType.equals( RdbValueType.VARCHAR ) || userType.equals( RdbValueType.TEXT )
					|| userType.equals( RdbValueType.CHARACTER ) )
				dbtype = userType;
			else {
				dbtype = RdbValueType.VARCHAR;
				changeUserDefinedDatabaseType( rdbSlot, dbtype );
			}
		} else if ( kbtype.equals( ValueType.SYMBOL ) ) {
			dbtype = RdbValueType.VARCHAR;
			if ( !userType.equals( RdbValueType.VARCHAR ) )
				changeUserDefinedDatabaseType( rdbSlot, dbtype );
		} else if ( kbtype.equals( ValueType.INSTANCE ) ) {
			dbtype = RdbValueType.FOREIGN_KEY;
		} else if ( kbtype.equals( ValueType.CLS ) )
			dbtype = RdbValueType.TEXT;
		else
			dbtype = RdbValueType.VARCHAR;

		return dbtype;
	}

	/**
	 * for each slot in the knowledge base, if slot is instance type, transform it into a foreign key else if it's a cls
	 * type, use its name for dictionary look up else map the slot to an attribute
	 */
	@SuppressWarnings("unchecked")
	protected void mapSlotsToAttributes() {
		for ( RdbSlot rdbSlot : _rdbSlots ) {
			if ( rdbSlot.getValueType().equals( ValueType.INSTANCE ) ) {
				if ( rdbSlot.referencesInlineCls() || rdbSlot.referenceDataTypeCls() ) { // in-lined complex type
					rdbSlot.setIsAssociated( false );
					_inlineSlots.add( rdbSlot );
					continue;
				} else if ( rdbSlot.isAssociated() ) // foreign keys
					_fkSlots.add( rdbSlot );
			} else if ( rdbSlot.getValueType().equals( ValueType.CLS ) ) {
				// only use the first parent of all the parents
				Collection parents = rdbSlot.getSlot().getAllowedParents();
				Cls parent = ((Cls)parents.iterator().next());
				String formParam = "CLS(" + parent.getName() + ")";

				rdbSlot.setFormWidget( FormWidget.SELECT.toString() );
				rdbSlot.setFormParameter( formParam );

				_warnings.add( rdbSlot.getName() + " widget parameter changed to " + formParam );
			}

			mapSlotToAttribute( rdbSlot );
		}
	}

	/**
	 * map slots of Instance type to foreign key Limitation: slots of abstract class is not handled
	 */
	protected void mapSlotsToForeignKeys() {
		Cls allowedCls;
		for ( RdbSlot fkRdbSlot : _fkSlots ) {
			allowedCls = fkRdbSlot.getAllowedCls();

			// if no allowed Cls exists, transform slot to default type
			if ( allowedCls == null ) {
				fkRdbSlot.setDefaultValueType();
				_fkSlots.remove( fkRdbSlot );
				_rdbSlots.add( fkRdbSlot );
				mapSlotToAttribute( fkRdbSlot );
				continue;
			} else if ( fkRdbSlot.getDomainCls().isAbstract() ) {
				// TODO
				System.err.println( "Foreign key slot -- " + fkRdbSlot.getName() + " references abstract class "
						+ fkRdbSlot.getDomainCls().getName()
						+ ". Nothing's done about it. You may want to create a rule to check for integrity later. " );
				continue;
			} else if ( allowedCls.isAbstract() && !_verticalFragmentation && isValidViewCls( allowedCls ) )
				mapSlotToViewKey( fkRdbSlot, allowedCls );
			else
				mapSlotToForeignKey( fkRdbSlot, allowedCls );
		}
	}



	@SuppressWarnings("unchecked")
	protected void mapSlotToAttribute( RdbSlot rdbSlot ) {
		Attribute att = null;
		Cls domainCls = rdbSlot.getDomainCls();
		String domainClsName = domainCls.getName();
		View view;
		Relation rel;

		// create an attribute
		if ( isValidRelationCls( domainCls ) ) {
			try {
				rel = _rdb.getRelation( domainClsName );
				att = new Attribute( rel, rdbSlot.getName(), mapProtegeValueTypeToRdbType( rdbSlot ), false, null );
				rel.addAttribute( att );

				// Slots of reified classes are primary keys of the bridging table
				if ( _kb.hasSuperclass( rdbSlot.getDomainCls(), _reifiedSlotCls ) )
					rel.setPrimaryKey( att );
			}
			catch (NoRelationFoundException nrf) {
				_warnings.add( "NoRelationFoundException for attribute " + rdbSlot.getName()
						+ " in mapSlotToAttribute: " + nrf.getLocalizedMessage() );
			}
		}

		// add slot to the view created for the corresponding table.
		if ( isValidViewCls( rdbSlot.getDomainCls() ) ) {
			try {
				view = _rdb.getView( getViewName( domainClsName ) );
				if ( att == null )
					att = new Attribute( view, rdbSlot.getName(), mapProtegeValueTypeToRdbType( rdbSlot ), false, null );

				view.addAttribute( att );
			}
			catch (NoViewFoundException nvf) {
				_errors.add( "error in mapSlotToAttribute() for " + rdbSlot.getName() + " : "
						+ nvf.getLocalizedMessage() );
			}
		}

		// set attribute meta-data
		if ( att != null ) {
			mapDatabaseParameter( rdbSlot, att );
			mapFormWidget( rdbSlot, att );
			mapViewWidget( rdbSlot, att );
			att.setFormWidgetParameter( rdbSlot.getFormWidgetParameter() );
			att.setIsRequired( rdbSlot.isRequired() );
			att.setDefaultValue( rdbSlot.getDefaultValue() );
			att.setAllowedValues( rdbSlot.getAllowedValues() );
			att.setMaximumValue( rdbSlot.getMaximumValue() );
			att.setMinimumValue( rdbSlot.getMinimumValue() );
			att.setIsImplemented( rdbSlot.isAssociated() );

			_modelMap.addMapping( rdbSlot, att );
		}
	}

	/** LIMIT slot to have only one type * */

	/*
	 * TODO if allowed cls is abstract, then map to view's id field and add check constraint because rdb doesn't allow
	 * foreign key to view's primary key if concrete, map to table using foreign key. this is implemented in RDB
	 */
	protected void mapSlotToForeignKey( RdbSlot slot, Cls allowedCls ) {
		Relation fromTable, toTable;
		try {
			fromTable = _rdb.getRelation( slot.getDomainCls().getName() );
			toTable = _rdb.getRelation( allowedCls.getName() );
			SimpleForeignKey fk = _rdb.addForeignKey( fromTable, slot.getName(), toTable,
					mapProtegeValueTypeToRdbType( slot ) );
			_modelMap.addMapping( slot, fk );
		}
		catch (NoRelationFoundException e) {
			_errors.add( "error in mapSlotToForeignKey: " + e.getMessage() + " while creating foreign key " + "["
					+ slot.getName() + "]" );
		}
	}

	/**
	 * For mapping slots that contain allowed class instances that are abstract. In non-vertically fragmented oo-rdb
	 * transformation, the subclass copies superclass's attributes. Abstract super classes do no exist physically as
	 * tables but as views. All foreign key references to these view primary keys are implemented as rules to check for
	 * existential integrity.
	 * 
	 * @param rdbSlot
	 * @param allowedCls allowed class of the value of the rdbSlot
	 */
	protected void mapSlotToViewKey( RdbSlot slot, Cls allowedCls ) {
		Relation fromTable;
		View toView;
		ViewReference vk;
		RdbValueType dbType = mapProtegeValueTypeToRdbType( slot );
		try {
			if ( isValidRelationCls( slot.getDomainCls() ) )
				fromTable = _rdb.getRelation( slot.getDomainCls().getName() );
			else
				fromTable = _rdb.getView( getViewName( slot.getDomainCls().getName() ) );

			toView = _rdb.getView( getViewName( allowedCls.getName() ) );
			vk = _rdb.addViewKey( fromTable, slot.getName(), toView, dbType );
			_modelMap.addMapping( slot, vk );
		}
		catch (NoRelationFoundException e) {
			_errors.add( "NoRelationFoundException in mapSlotToViewKey [" + slot.getDomainCls().getName() + "].["
					+ slot.getName() + "] " );
		}
		catch (NoViewFoundException e1) {
			_errors.add( "NoViewFoundException in mapSlotToViewKey [" + slot.getName() + "] to ["
					+ getViewName( allowedCls.getName() ) + "] " );
		}
	}

	/**
	 * reify the slots with maximum cardinality (cardinality > 1) createAssociationCls (which will be transformed to
	 * table) for slot with max multiplevalues in general this is done for any value with multiple values types TODO
	 * create primary key that's combination of the two foreign keys (no ID field)
	 * 
	 * @param slot
	 */
	protected Cls reifySlot( RdbSlot rdbSlot ) {
		String targetClsName, sourceClsName, associationClsName;
		ValueType targetClsValueType;
		boolean isTargetPrimitive = false;

		// target class can be either primitive type or cls instance
		Cls targetCls = rdbSlot.getAllowedCls();
		if ( targetCls == null )
			isTargetPrimitive = true;

		if ( !isTargetPrimitive ) {
			targetClsName = rdbSlot.getAllowedCls().getName();
			targetClsValueType = ValueType.INSTANCE;
		} else {
			targetClsName = rdbSlot.getValueType().toString();
			targetClsValueType = rdbSlot.getValueType();
		}

		sourceClsName = rdbSlot.getDomainCls().getName();
		associationClsName = sourceClsName + SPECIAL_CONNECTOR + rdbSlot.getName() + SPECIAL_CONNECTOR + targetClsName;

		// create UNIQUE named association cls
		Cls associationCls = _kb.getCls( associationClsName );

		// delete the duplicate assoc cls and recreate it
		if ( associationCls != null
				&& !isAssociationClsForReifiedSlot( associationCls, rdbSlot, targetClsName, sourceClsName ) ) {
			deleteClsAndSlots( associationCls );
			_errors.add( "Association class with name " + associationClsName
					+ " already exists. It will be deleted and re-created as the result of transformation." );
		}

		associationCls = createAssociationCls( rdbSlot, associationClsName, targetClsName, sourceClsName,
				targetClsValueType );

		RdbCls assocRdbCls = new RdbCls( associationCls, _kb );
		ensureValidNameLength( assocRdbCls );
		addReifiedSlot( rdbSlot, assocRdbCls );
		rdbSlot.getSlot().setVisible( true );
		return associationCls;
	}
	
	/**
	 * create mapping between slots reified & their respective rdbCls
	 */
	protected void mapSlotsToRelations() {
		Iterator<RdbSlot> slotIter = _reifiedSlots.keySet().iterator();
		while (slotIter.hasNext()) {
			RdbSlot rdbSlot = slotIter.next();
			RdbCls assocRdbCls = _reifiedSlots.get( rdbSlot );
			_modelMap.addMapping( rdbSlot, assocRdbCls );
		}
	}

	protected void reifySlotsWithMaxCardinality() {
		RdbSlot rdbSlot;
		for ( int i = 0; i < _rdbSlots.size(); i++ ) {
			rdbSlot = (RdbSlot)_rdbSlots.elementAt( i );

			if ( isSlotReifiable( rdbSlot ) )
				reifySlot( rdbSlot );
		}
	}

	@SuppressWarnings("unchecked")
	public class InvalidSeedpodProjectException extends Exception {
		static final long serialVersionUID = 1L;
		private Collection _errors;

		public InvalidSeedpodProjectException(Collection errors) {
			_errors = errors;
		}

		public Collection getErrorMessages() {
			return _errors;
		}

		public String printMessages() {
			String msg = new String();
			for ( Iterator iter = _errors.iterator(); iter.hasNext(); ) {
				msg += (String)iter.next();
			}
			return msg;
		}
	}

}